# Blender FLIP Fluid Add-on
# Copyright (C) 2019 Ryan L. Guy
# 
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

import bpy
from bpy.props import (
        BoolProperty,
        FloatProperty,
        PointerProperty,
        EnumProperty
        )

from .custom_properties import (
        NewMinMaxFloatProperty
        )

from . import preset_properties
from .. import types
from ..utils import version_compatibility_utils as vcu


class FlipFluidForceFieldProperties(bpy.types.PropertyGroup):
    conv = vcu.convert_attribute_to_28

    force_field_type = EnumProperty(
            name="Type",
            description="Type of force field",
            items=types.force_field_types,
            default='FORCE_FIELD_TYPE_POINT',
            options={'HIDDEN'},
            ); exec(conv("force_field_type"))
    is_enabled = BoolProperty(
            name="Enabled",
            description="Object contributes to the fluid simulation",
            default=True,
            ); exec(conv("is_enabled"))
    strength = FloatProperty(
            name="Strength",
            description="Strength of the force field",
            default=9.81,
            precision=2,
            ); exec(conv("strength"))
    falloff_power = FloatProperty(
            name="Falloff Power",
            description="How quickly force strength decreases with distance. If "
                " r is the distance from the force object, the force strength changes"
                " with (1 / r^power). A value of 0 = constant force, 1 = linear falloff,"
                " 2 = gravitational falloff",
            default=1.0,
            min=0.0,
            soft_max=3.0, max=6.0,
            precision=2,
            ); exec(conv("falloff_power"))
    enable_min_distance = BoolProperty(
            name="Enable Min Distance",
            description="Use a minimum distance for the force field falloff",
            default=False,
            ); exec(conv("enable_min_distance"))
    enable_max_distance = BoolProperty(
            name="Enable Max Distance",
            description="Use a maximum distance for the force field to work",
            default=False,
            ); exec(conv("enable_max_distance"))
    min_max_distance = NewMinMaxFloatProperty(
            name_min="Min Distance", 
            description_min="The distance from the force object at which the strength"
                " begins to falloff. Field strengths can become very high at small distances."
                " Increase to limit small distances", 
            min_min=0,
            default_min=0.0,
            precision_min=3,

            name_max="Max Distance", 
            description_max="Maximum distance from the force object that the force"
                " field will have an effect on the simulation. Limiting max distance"
                " can help speed up force field calculations", 
            min_max=0,
            default_max=0.0,
            precision_max=3,
            ); exec(conv("min_max_distance"))
    export_animated_mesh = bpy.props.BoolProperty(
            name="Export Animated Mesh",
            description="Export this mesh as an animated one (slower, only"
                " use if really necessary [e.g. armatures or parented objects],"
                " animated pos/rot/scale F-curves do not require it",
            default=False,
            options={'HIDDEN'},
            ); exec(conv("export_animated_mesh"))
    skip_animated_mesh_reexport = BoolProperty(
            name="Skip re-export",
            description="Skip re-exporting this mesh when starting or resuming"
                " a bake. If this mesh has not been exported or is missing files,"
                " the addon will automatically export the required files",
            default=False,
            options={'HIDDEN'},
            ); exec(conv("skip_animated_mesh_reexport"))

    maximum_strength_tooltip = BoolProperty(
            name="Maximum Strength", 
            description="This value estimates the maximum possible force field strength"
                " generated by this object. Force field strengths are inversely proportional"
                " to distance and can become very large as distances decrease."
                " Use the Minimum Distance option to limit small distances and reduce"
                " maximum force field strength. For reference, a strength value of 9.81"
                " is equal to the default strength of gravity", 
            default=True,
            ); exec(conv("maximum_strength_tooltip"))

    property_registry = PointerProperty(
            name="Outflow Property Registry",
            description="",
            type=preset_properties.PresetRegistry,
            ); exec(conv("property_registry"))


    def initialize(self):
        self.property_registry.clear()
        add = self.property_registry.add_property
        add("force_field.force_field_type", "")
        add("force_field.is_enabled", "")
        add("force_field.strength", "")
        add("force_field.falloff_power", "")
        add("force_field.enable_min_distance", "")
        add("force_field.enable_max_distance", "")
        add("force_field.min_max_distance", "")
        add("force_field.export_animated_mesh", "")
        self._validate_property_registry()


    def _validate_property_registry(self):
        for p in self.property_registry.properties:
            path = p.path
            base, identifier = path.split('.', 1)
            if not hasattr(self, identifier):
                print("Property Registry Error: Unknown Identifier <" + 
                      identifier + ", " + path + ">")


    def load_post(self):
        self.initialize()


def load_post():
    force_field_objects = bpy.context.scene.flip_fluid.get_force_field_objects()
    for force_field in force_field_objects:
        force_field.flip_fluid.force_field.load_post()


def register():
    bpy.utils.register_class(FlipFluidForceFieldProperties)


def unregister():
    bpy.utils.unregister_class(FlipFluidForceFieldProperties)